claude-code/spec/07_hooks.md
2026-04-01 01:20:27 +05:30

82 KiB
Raw Permalink Blame History

Claude Code — React Hooks

This document covers every hook in src/hooks/, src/hooks/toolPermission/, and src/hooks/notifs/. For each hook the entry covers: purpose, parameters/props, return value, key logic and side effects, and dependencies.


Table of Contents

  1. Core / Utility Hooks
  2. Input & Text Editing Hooks
  3. Permission & Tool-Use Hooks
  4. Swarm / Teammate Hooks
  5. IDE Integration Hooks
  6. Remote & Session Hooks
  7. Plugin & Suggestion Hooks
  8. Notification Hooks (notifs/)
  9. Tool Permission Subsystem (toolPermission/)
  10. Non-Hook Utilities in hooks/

Core / Utility Hooks

useAfterFirstRender

File: hooks/useAfterFirstRender.ts

Purpose: ANT-internal startup-time measurement hook. After the first render it writes startup time to stderr and calls process.exit(0) if the CLAUDE_CODE_EXIT_AFTER_FIRST_RENDER environment variable is set.

Parameters: none

Return Value: void

Key Logic:

  • Reads env var CLAUDE_CODE_EXIT_AFTER_FIRST_RENDER.
  • Uses a useEffect on [] to fire after first commit.
  • Computes elapsed ms from MACRO.STARTUP_TIMESTAMP, writes to process.stderr, then exits.

Dependencies: useEffect (React)


useApiKeyVerification

File: hooks/useApiKeyVerification.ts

Purpose: Manages the full lifecycle of API key verification — loading, valid, invalid, missing, or error — and exposes a reverify callback. Guards against running apiKeyHelper scripts before the trust dialog is dismissed to prevent RCE.

Parameters: none

Return Value: ApiKeyVerificationResult{ status: 'loading'|'valid'|'invalid'|'missing'|'error', reverify: () => void, errorMessage?: string }

Key Logic:

  • Subscribes to AppState for trustDialogAccepted and apiKeyVerificationStatus.
  • Uses useEffect to run the verification on mount and whenever reverify is called.
  • Skips the apiKeyHelper process before the trust dialog is shown.
  • Returns a stable reverify callback via useCallback.

Dependencies: useAppState, useSetAppState, useCallback, useEffect


File: hooks/useBlink.ts

Purpose: Returns a blinking boolean flag synchronized with an animation-frame clock. Pauses when the terminal is blurred or the component is offscreen (OffscreenFreeze).

Parameters:

  • enabled: boolean — when false, always returns true (cursor always visible).
  • intervalMs?: number — blink period in milliseconds (default: 530).

Return Value: [ref: RefObject<unknown>, isVisible: boolean]

Key Logic:

  • Uses useAnimationFrame (Ink hook) to read the shared clock counter.
  • Divides counter by intervalMs / frameMs and toggles on even/odd.
  • Returns same ref from Ink's useOffscreenFreeze to pause when not in viewport.

Dependencies: useAnimationFrame (ink), useTerminalFocus (ink), useRef, useMemo


useCommandQueue

File: hooks/useCommandQueue.ts

Purpose: Exposes the current unified command queue as a reactive array. Any component can subscribe to observe queued commands without managing external store subscriptions manually.

Parameters: none

Return Value: readonly QueuedCommand[]

Key Logic:

  • Wraps useSyncExternalStore over messageQueueManager's subscribe/getSnapshot pair.
  • Re-renders only when the queue reference changes (not on every push that doesn't change length).

Dependencies: useSyncExternalStore (React), messageQueueManager


useCopyOnSelect

File: hooks/useCopyOnSelect.ts

Purpose: Automatically copies selected text to the clipboard when the user releases the mouse (mouseup) or double/triple-clicks. Also exports useSelectionBgColor for theming selected text.

Parameters:

  • selection: SelectionState — current ink selection state.
  • isActive: boolean — only active when true.
  • onCopied?: () => void — callback fired after clipboard write.

Return Value: void

Key Logic:

  • Subscribes to ink mouse events via useEffect.
  • On mouseup: if selection is non-empty and isActive, calls navigator.clipboard.writeText.
  • useSelectionBgColor() reads AppState theme to return the correct highlight color.

Dependencies: useEffect, useAppState, useCallback


useDoublePress

File: hooks/useDoublePress.ts

Purpose: Returns a callback that implements double-press detection within an 800 ms window. Used for Ctrl+C/D to exit and double-Escape to clear input.

Parameters:

  • setPending: (show: boolean) => void — called with true after first press, false after timeout.
  • onDoublePress: () => void — called when the second press occurs within the window.
  • onFirstPress?: () => void — optional side effect on first press.

Return Value: () => void — the wrapped press handler

Key Logic:

  • Tracks lastPressTime in a ref.
  • On call: if elapsed < 800 ms, calls onDoublePress and resets; otherwise calls setPending(true), sets a 800 ms timer to call setPending(false), and optionally calls onFirstPress.

Dependencies: useRef, useCallback


useElapsedTime

File: hooks/useElapsedTime.ts

Purpose: Computes a human-readable elapsed time string (e.g. "1m 23s") that updates while a task is running and freezes once it ends.

Parameters:

  • startTime: number — Unix timestamp (ms) when timing started.
  • isRunning: boolean — when false, elapsed is frozen.
  • ms?: number — update interval in ms (default: 1000).
  • pausedMs?: number — accumulated paused time to subtract.
  • endTime?: number — if provided, freezes at this timestamp.

Return Value: string — formatted elapsed time like "5s", "1m 23s", "2h 5m".

Key Logic:

  • Uses useSyncExternalStore over a timer-based external clock.
  • Clock updates every ms via setInterval; each subscriber gets a stable snapshot until it ticks.
  • Formats the delta using formatDuration.

Dependencies: useSyncExternalStore, useRef


useExitOnCtrlCD

File: hooks/useExitOnCtrlCD.ts

Purpose: Implements double-press Ctrl+C / Ctrl+D to exit. Returns pending state so callers can show a "Press again to exit" hint.

Parameters:

  • useKeybindingsHook: (bindings: ...) => void — injectable hook for binding.
  • onInterrupt?: () => void — called on first Ctrl+C press.
  • onExit?: () => void — called on second press.
  • isActive?: boolean — enables/disables the handler.

Return Value: ExitState{ pending: boolean, keyName: string | null }

Key Logic:

  • Uses useDoublePress internally for both Ctrl+C and Ctrl+D.
  • Sets pending = true after first press; false after timeout or second press.
  • keyName tracks which key was pressed ('Ctrl-C' or 'Ctrl-D').

Dependencies: useDoublePress, useState, useCallback


useExitOnCtrlCDWithKeybindings

File: hooks/useExitOnCtrlCDWithKeybindings.ts

Purpose: Convenience wrapper that wires useExitOnCtrlCD to the keybinding system.

Parameters:

  • onExit?: () => void
  • onInterrupt?: () => void
  • isActive?: boolean

Return Value: ExitState

Key Logic: Passes useKeybindings as the hook parameter to useExitOnCtrlCD.

Dependencies: useExitOnCtrlCD, useKeybindings


useMemoryUsage

File: hooks/useMemoryUsage.ts

Purpose: Polls Node.js process.memoryUsage().heapUsed every 10 seconds and returns a status when memory usage is high or critical.

Parameters: none

Return Value: MemoryUsageInfo | nullnull for normal; { heapUsed: number, status: 'high' | 'critical' } when heap > 1.5 GB (high) or > 2.5 GB (critical).

Key Logic:

  • Uses useInterval (usehooks-ts) with 10 000 ms period.
  • Thresholds: HIGH_HEAP_MB = 1536, CRITICAL_HEAP_MB = 2560.
  • Returns null when below thresholds.

Dependencies: useInterval, useState, useEffect


useMinDisplayTime

File: hooks/useMinDisplayTime.ts

Purpose: Prevents UI flicker by guaranteeing each distinct value stays visible for at least minMs milliseconds before switching.

Parameters:

  • value: T — the value to display.
  • minMs: number — minimum display duration.

Return Value: T — the "stable" displayed value, may lag behind value.

Key Logic:

  • Uses useRef to track the current stable value and the timestamp it was set.
  • On value change: if Date.now() - lastChanged >= minMs, updates immediately; otherwise schedules a setTimeout to update after the remainder.

Dependencies: useState, useRef, useEffect


useNotifyAfterTimeout

File: hooks/useNotifyAfterTimeout.ts

Purpose: Sends a desktop (OS-level) notification after 6 seconds of user inactivity — used to alert the user when Claude has been working unattended.

Parameters:

  • message: string — the notification body text.
  • notificationType: string — identifies the event type for analytics.

Return Value: void

Key Logic:

  • Waits 6 000 ms after mount using setTimeout.
  • Checks terminal focus state; only fires if the terminal is not focused.
  • Calls sendDesktopNotification from a native module.

Dependencies: useEffect, useRef


useTimeout

File: hooks/useTimeout.ts

Purpose: Returns a boolean that becomes true after delay ms. Resets when resetTrigger changes.

Parameters:

  • delay: number — ms to wait.
  • resetTrigger?: number — changing this value resets the timer.

Return Value: booleanfalse until the delay elapses, then true.

Key Logic: Simple useState + useEffect with setTimeout. Cleanup clears the timeout on re-run or unmount.

Dependencies: useState, useEffect


useSettings

File: hooks/useSettings.ts

Purpose: Reads the current settings from global AppState. Reactive — re-renders when settings change (e.g. file-watcher triggers).

Parameters: none

Return Value: ReadonlySettings

Key Logic: Returns useAppState(s => s.settings).

Dependencies: useAppState


useSettingsChange

File: hooks/useSettingsChange.ts

Purpose: Subscribes to the settings change detector and calls onChange with the new settings and the change source whenever the settings file is modified on disk.

Parameters:

  • onChange: (source: string, settings: Settings) => void

Return Value: void

Key Logic:

  • Uses useEffect to subscribe to settingsChangeDetector.subscribe(onChange).
  • Returns the unsubscribe function as the cleanup.

Dependencies: useEffect, settingsChangeDetector


useDeferredHookMessages

File: hooks/useDeferredHookMessages.ts

Purpose: Injects SessionStart hook messages into the message list asynchronously on mount, avoiding blocking the first render.

Parameters:

  • pendingHookMessages: Message[] — messages generated by session-start hooks.
  • setMessages: SetMessages — the message list updater.

Return Value: () => Promise<void> — a stable async callback to trigger injection.

Key Logic:

  • Defers via setTimeout(0) to let the first render complete before injecting hook messages.
  • Uses useRef to avoid stale closure issues.

Dependencies: useRef, useCallback


useDiffData

File: hooks/useDiffData.ts

Purpose: Fetches current git diff statistics and hunks on mount (used by the /diff command view).

Parameters: none

Return Value: DiffData{ stats: DiffStats, files: string[], hunks: DiffHunk[], loading: boolean }

Key Logic:

  • On mount, calls getGitDiff() which runs git diff in the cwd.
  • Sets loading: true until the async fetch completes.

Dependencies: useState, useEffect


useFileHistorySnapshotInit

File: hooks/useFileHistorySnapshotInit.ts

Purpose: One-time initialization of the file history state from snapshot data stored in the conversation log, restoring file timestamps across /resume.

Parameters:

  • initialFileHistorySnapshots: FileHistorySnapshot[]
  • fileHistoryState: FileHistoryState
  • onUpdateState: (state: FileHistoryState) => void

Return Value: void

Key Logic:

  • Uses useEffect with [] dep to run only once.
  • Merges initialFileHistorySnapshots into fileHistoryState without overwriting newer entries.

Dependencies: useEffect


useInputBuffer

File: hooks/useInputBuffer.ts

Purpose: Provides a debounced undo buffer for text input, enabling "undo last paste" or "undo last edit" functionality.

Parameters:

  • maxBufferSize: number — maximum number of entries to keep.
  • debounceMs: number — how long to wait before committing current value.

Return Value: UseInputBufferResult{ pushToBuffer, undo, canUndo, clearBuffer }

Key Logic:

  • Maintains a string[] undo stack in a ref.
  • pushToBuffer is debounced: multiple rapid changes collapse into one buffer entry.
  • undo pops the stack and calls onChange with the previous value.

Dependencies: useRef, useCallback, useEffect


useLogMessages

File: hooks/useLogMessages.ts

Purpose: Incrementally records messages to the conversation transcript file (.jsonl) after each render. Avoids re-writing the full transcript on every update.

Parameters:

  • messages: readonly Message[] — the current message list.
  • ignore?: boolean — when true, skips recording.

Return Value: void

Key Logic:

  • Tracks lastProcessedIndex in a ref to process only new messages.
  • Handles edge cases: compaction (transcript size shrinks), first render, head-pointer rewind.
  • Calls recordTranscript(messages, from, to) for new messages only.
  • Deduplicates compact-summary boundaries.

Dependencies: useEffect, useRef


useMainLoopModel

File: hooks/useMainLoopModel.ts

Purpose: Returns the resolved model name for the current session. Re-evaluates when GrowthBook flags are refreshed so model alias resolution stays current mid-session.

Parameters: none

Return Value: ModelName

Key Logic:

  • Reads settings.model from AppState.
  • Subscribes to onGrowthBookRefresh via useEffect; on each refresh, forces a re-render by incrementing a counter state.
  • Calls resolveModelAlias(model) to translate user-facing aliases (e.g. opus) to concrete model IDs.

Dependencies: useAppState, useEffect, useState


useManagePlugins

File: hooks/useManagePlugins.ts

Purpose: Loads the plugin list on mount and wires up plugin lifecycle management: delisting enforcement, MCP/LSP plugin counting, and refresh-needed notifications.

Parameters:

  • { enabled?: boolean }

Return Value: void

Key Logic:

  • On mount (if enabled): calls loadPlugins() and writes results to AppState.
  • Enforces delisted plugin removal by reading delistedPlugins from settings.
  • Counts active MCP and LSP plugins and writes totals to AppState for /doctor diagnostics.
  • Does NOT auto-refresh; refresh is triggered explicitly via /reload-plugins.

Dependencies: useEffect, useSetAppState, useAppState


useMergedClients

File: hooks/useMergedClients.ts

Purpose: Deduplicates two MCP client lists (initial from settings + dynamically loaded) by server name.

Parameters:

  • initialClients: MCPServerConnection[]
  • mcpClients: MCPServerConnection[]

Return Value: MCPServerConnection[]

Key Logic: Uses lodash.uniqBy([...initialClients, ...mcpClients], 'name'). The useMemo dependency is the combined list length and name set.

Dependencies: useMemo, lodash.uniqBy


useMergedCommands

File: hooks/useMergedCommands.ts

Purpose: Deduplicates command lists from initial load and MCP-sourced commands by command name.

Parameters:

  • initialCommands: Command[]
  • mcpCommands: Command[]

Return Value: Command[]

Key Logic: useMemo over uniqBy([...initialCommands, ...mcpCommands], getCommandName).

Dependencies: useMemo


useMergedTools

File: hooks/useMergedTools.ts

Purpose: Assembles the full tool pool for a session by combining built-in tools, MCP tools, and applying permission-context filtering.

Parameters:

  • initialTools: Tool[]
  • mcpTools: Tool[]
  • toolPermissionContext: ToolPermissionContext

Return Value: Tools (the assembled tool set)

Key Logic:

  • Calls assembleToolPool(initialTools, mcpTools) to build the combined list.
  • Then calls mergeAndFilterTools(pool, toolPermissionContext) to remove disabled tools.

Dependencies: useMemo


useSkillsChange

File: hooks/useSkillsChange.ts

Purpose: Keeps the command list fresh when skill files change on disk or when GrowthBook flags are refreshed.

Parameters:

  • cwd: string | undefined — the current working directory for scanning skills.
  • onCommandsChange: (commands: Command[]) => void — callback to update the command list.

Return Value: void

Key Logic:

  • Subscribes to skillChangeDetector.subscribe(handleChange) — fires on skill file writes.
  • On file change: calls clearCommandsCache() + getCommands(cwd) and calls onCommandsChange.
  • Subscribes to onGrowthBookRefresh(handleGrowthBookRefresh) — on GB flag refresh, calls clearCommandMemoizationCaches() + getCommands(cwd) to re-evaluate feature-gated commands.

Dependencies: useEffect, useCallback


useUpdateNotification

File: hooks/useUpdateNotification.ts

Purpose: Returns the new semantic version string when an auto-update has been downloaded, for display in the status bar. Returns null if no new version or the version hasn't changed since last notification.

Parameters:

  • updatedVersion: string | null | undefined — the downloaded version (from auto-updater).
  • initialVersion?: string — baseline version (default: MACRO.VERSION).

Return Value: string | null

Key Logic:

  • Parses both versions with semver to extract major.minor.patch.
  • Uses useState to track the last-notified semver.
  • If the new semver differs from lastNotifiedSemver, sets state and returns the new value (triggers notification display). Otherwise returns null.

Dependencies: useState, semver


Input & Text Editing Hooks

useTextInput

File: hooks/useTextInput.ts

Purpose: Full readline-style text input handler. Manages cursor position, multiline editing, kill ring (Ctrl+K/U/W), yank (Ctrl+Y / Meta+Y), history navigation (Up/Down arrows), and ghost text rendering.

Parameters: UseTextInputProps including:

  • value: string — current text value (controlled).
  • onChange: (value: string) => void
  • onSubmit?: (value: string) => void
  • onExit?: () => void
  • onHistoryUp / onHistoryDown / onHistoryReset / onClearInput
  • focus?: boolean
  • mask?: string — masks all chars with this string.
  • multiline?: boolean
  • cursorChar: string
  • columns: number — terminal width for wrapping.
  • externalOffset: number — cursor offset controlled externally.
  • onOffsetChange: (offset: number) => void
  • inputFilter?: (input: string, key: Key) => string
  • inlineGhostText?: InlineGhostText
  • disableCursorMovementForUpDownKeys?: boolean
  • disableEscapeDoublePress?: boolean
  • maxVisibleLines?: number

Return Value: TextInputState{ onInput, renderedValue, offset, setOffset, cursorLine, cursorColumn, viewportCharOffset, viewportCharEnd }

Key Logic:

  • Cursor class from utils/Cursor.js manages the text buffer and position arithmetic.
  • Maps keypresses to cursor mutations: Ctrl+A (home), Ctrl+E (end), Ctrl+F/B (forward/back), Ctrl+N/P (next/prev line), Meta+F/B (word navigation).
  • Kill ring: Ctrl+K (kill to end), Ctrl+U (kill to start), Ctrl+W (kill word). Successive kills append to ring.
  • Yank: Ctrl+Y inserts last kill; Meta+Y cycles through the ring.
  • Double-press Ctrl+C clears or exits (via useDoublePress).
  • Double-press Escape clears input with "Esc again to clear" hint.
  • SSH-coalesced Enter detection: text\r form triggers submit.
  • Handles raw \x7f DEL characters for SSH/tmux compatibility.
  • Inline ghost text rendered at cursor position when inlineGhostText.insertPosition === offset.

Dependencies: useDoublePress, useNotifications, Cursor class, useCallback


useVimInput

File: hooks/useVimInput.ts

Purpose: Extends useTextInput with a full Vim normal/insert mode state machine, including operators (d, c, y), motions, dot-repeat, find (f/F/t/T), text objects (iw, aw, etc.), and yank register.

Parameters: UseVimInputProps — same as UseTextInputProps plus:

  • onModeChange?: (mode: VimMode) => void
  • onUndo?: () => void

Return Value: VimInputState — extends TextInputState with { mode: VimMode, setMode }

Key Logic:

  • Delegates INSERT mode keypresses to useTextInput after running inputFilter.
  • In NORMAL mode, dispatches keypresses through transition(state.command, input, ctx) from vim/transitions.ts.
  • Manages vimStateRef (current mode + pending command accumulator) and persistentRef (register, lastFind, lastChange for dot-repeat).
  • Escape in INSERT: switchToNormalMode() moves cursor left by one.
  • Arrow keys in NORMAL: mapped to h/j/k/l motions.
  • ? in NORMAL idle: enters / search by writing ? to the input.
  • setModeExternal allows callers to programmatically switch modes (used by /vim command).

Dependencies: useTextInput, useState, useRef, useCallback, vim operators/transitions


useSearchInput

File: hooks/useSearchInput.ts

Purpose: Full readline-style text input for search boxes (history search, global search). Includes kill ring, yank, and word navigation.

Parameters: UseSearchInputOptions{ initialValue?, onKeyDown?, placeholder? }

Return Value: { query: string, setQuery, cursorOffset: number, handleKeyDown: (key: Key, input: string) => void }

Key Logic:

  • Implements the same Ctrl key mapping as useTextInput but as a standalone reducer without React state for the cursor offset.
  • Used in HistorySearchInput and GlobalSearchDialog.

Dependencies: useState, useCallback, useRef, kill ring utilities


useArrowKeyHistory

File: hooks/useArrowKeyHistory.tsx

Purpose: Arrow-key navigation through input history with lazy chunked loading, mode-based filtering, and draft preservation.

Parameters:

  • onSetInput: (value: string) => void
  • currentInput: string
  • pastedContents: string[] — paste-detected content to exclude from history matching.
  • setCursorOffset?: (offset: number) => void
  • currentMode?: PromptInputMode

Return Value: { handleHistoryUp, handleHistoryDown, handleHistoryReset }

Key Logic:

  • Loads history lazily in chunks of 50 from getHistory().
  • Navigates using an index pointer; on first Up saves the current draft.
  • Filters entries by mode (e.g., bash mode only returns bash history).
  • Shows a "Search history: Ctrl+R" hint notification on first use.
  • Resets index when currentInput changes externally.

Dependencies: useState, useRef, useCallback, useNotifications


useHistorySearch

File: hooks/useHistorySearch.ts

Purpose: Implements Ctrl+R backward incremental history search with query matching and keyboard navigation.

Parameters:

  • onSetInput: (value: string) => void
  • currentInput: string
  • plus keybinding options.

Return Value: { historyQuery, setHistoryQuery, historyMatch, historyFailedMatch, handleKeyDown }

Key Logic:

  • Registers history:search keybinding (Ctrl+R) to activate search mode.
  • In search mode, registers historySearch:* bindings (Enter to confirm, Escape to cancel, up/down to cycle matches).
  • Filters history entries by substring match against historyQuery.
  • historyFailedMatch: boolean — true when query has text but no match.

Dependencies: useKeybinding, useKeybindings, useState, useCallback, useEffect


useTypeahead

File: hooks/useTypeahead.tsx

Purpose: The primary typeahead/autocomplete engine for the prompt input. Handles @file, /command, #channel, and directory suggestions using debounced fuzzy matching, shell completion, and MCP resources.

Parameters: Large props object including:

  • inputValue: string, cursorOffset: number
  • commands: Command[], agents: AgentDefinition[]
  • mcpResources: MCPResource[]
  • isLoading: boolean
  • onSelect: (value: string) => void
  • onToggleVisible: (show: boolean) => void

Return Value: { suggestions, selectedIndex, handleKeyDown, isSuggesting, suggestionType, ... }

Key Logic:

  • Detects suggestion context from input: @token triggers file/resource/agent suggestions; / triggers command suggestions; #channel triggers Slack channel suggestions (if Slack MCP present).
  • File suggestions use generateUnifiedSuggestions (nucleo + Fuse.js ranked).
  • Command suggestions use generateCommandSuggestions with argument hint generation.
  • Shell completions use getShellCompletions for bash/zsh completions.
  • Path completions use getPathCompletions / getDirectoryCompletions.
  • Registers as an overlay via useRegisterOverlay so escape/enter/arrow keys are captured.
  • Uses useDebounceCallback (usehooks-ts) to rate-limit file lookups.
  • Tracks keyboard navigation state (selectedIndex) internally.
  • Session resume suggestions for /resume queries via searchSessionsByCustomTitle.

Dependencies: useInput (ink, backward-compat bridge), useRegisterOverlay, useKeybindings, useDebounceCallback, useState, useRef, useMemo, useCallback, useEffect, generateUnifiedSuggestions, generateCommandSuggestions, getShellCompletions


usePasteHandler

File: hooks/usePasteHandler.ts

Purpose: Handles bracketed paste mode detection, large paste chunking, image file path detection, and macOS clipboard image fallback.

Parameters:

  • { onPaste: (text: string) => void, onInput: (text: string, key: Key) => void, onImagePaste?: (base64: string, ...) => void }

Return Value: { wrappedOnInput, pasteState, isPasting }

Key Logic:

  • Detects bracketed paste via \x1b[?2004h / \x1b[200~ / \x1b[201~ escape sequences.
  • Splits large pastes (>1000 chars) into multiple onPaste calls to avoid blocking the event loop.
  • Detects image file paths in paste content (extensions .png, .jpg, .gif, etc.) and triggers onImagePaste.
  • On macOS: falls back to pbpaste for image clipboard content.

Dependencies: useState, useRef, useCallback, useEffect


useVoice

File: hooks/useVoice.ts

Purpose: Hold-to-talk voice recording using the voice_stream STT endpoint. Auto-repeat key events extend the recording; releasing the key after RELEASE_TIMEOUT_MS stops it.

Parameters: { onTranscript: (text: string) => void, enabled: boolean }

Return Value: { state: 'idle'|'recording'|'processing', handleKeyEvent: (fallbackMs?: number) => void }

Key Logic:

  • Calls connectVoiceStream() to open a WebSocket to voice_stream STT.
  • Maps user locale to BCP-47 language codes for Deepgram (20+ languages mapped).
  • Auto-repeat detection: key events arriving within 120 ms are considered "held".
  • Modifier combos (Ctrl+Space etc.) use 2 000 ms FIRST_PRESS_FALLBACK_MS.
  • Requires 5 rapid keydowns (HOLD_THRESHOLD) for bare-char bindings to activate, 2 for warmup feedback.
  • Uses useTerminalFocus to pause recording when terminal loses focus.
  • Fetches voice keyterms for improved domain recognition.

Dependencies: useState, useRef, useCallback, useEffect, useTerminalFocus, connectVoiceStream, getVoiceKeyterms


useVoiceEnabled

File: hooks/useVoiceEnabled.ts

Purpose: Combines user intent (settings.voiceEnabled), OAuth auth check, and GrowthBook kill-switch into a single boolean indicating whether voice mode is available.

Parameters: none

Return Value: boolean

Key Logic:

  • userIntent from useAppState(s => s.settings.voiceEnabled === true).
  • authed memoized on authVersion — avoids expensive hasVoiceAuth() call on every render.
  • isVoiceGrowthBookEnabled() not memoized (cheap cached lookup, so mid-session kill-switch takes effect).

Dependencies: useAppState, useMemo


useVoiceIntegration

File: hooks/useVoiceIntegration.tsx

Purpose: Orchestrates the full voice-mode integration: reading keybindings, detecting held keys, activating useVoice, suppressing full-width space input during recording, and showing status notifications.

Parameters:

  • { onTranscript: (text: string) => void, isModalOverlayActive: boolean }

Return Value: { voiceState: VoiceState, handleVoiceKeyEvent }

Key Logic:

  • Reads voice:activate keybinding from keybinding context (default: spacebar).
  • Detects held key by counting rapid key events (HOLD_THRESHOLD=5 for bare chars, 1 for modifier combos).
  • Shows warmup notification after WARMUP_THRESHOLD=2 events.
  • Uses useInput (ink) as a backward-compat bridge until REPL wires handleKeyDown to <Box onKeyDown>.
  • Guards on useIsModalOverlayActive() — does not activate voice while a modal is open.
  • Dead-code elimination: conditionally requires useVoice only if feature('VOICE_MODE') is set; otherwise uses a no-op stub.
  • Calls normalizeFullWidthSpace to handle full-width space (Japanese IME) by passing it to the transcript instead of activating voice.

Dependencies: useVoice, useVoiceEnabled, useInput (ink), useOptionalKeybindingContext, useIsModalOverlayActive, useNotifications, useState, useRef, useMemo, useCallback, useEffect


useVirtualScroll

File: hooks/useVirtualScroll.ts

Purpose: React-level virtualization for MessageRow items inside a ScrollBox. Mounts only items in the viewport plus overscan, using spacer boxes to maintain scroll height.

Parameters:

  • scrollRef: RefObject<ScrollBoxHandle | null> — reference to the ScrollBox.
  • itemKeys: readonly string[] — stable keys for each item.
  • columns: number — terminal width; triggers height cache rescaling on change.

Return Value: VirtualScrollResult:

  • range: [startIndex, endIndex) — half-open slice to render.
  • topSpacer: number — rows before the first rendered item.
  • bottomSpacer: number — rows after last rendered item.
  • measureRef: (key) => ref — attach to each item root Box for height measurement.
  • spacerRef: RefObject<DOMElement> — attach to top spacer for drift-free origin tracking.
  • offsets: ArrayLike<number> — cumulative y-offsets per item.
  • getItemTop: (index) => number — reads live Yoga computedTop.
  • getItemElement: (index) => DOMElement | null
  • getItemHeight: (index) => number | undefined
  • scrollToIndex: (i) => void

Key Logic:

  • DEFAULT_ESTIMATE = 3 rows for unmeasured items; OVERSCAN_ROWS = 80; COLD_START_COUNT = 30.
  • SCROLL_QUANTUM = 40 rows — scrollTop is quantized so React re-renders only when the mounted range needs to shift, not on every wheel tick.
  • SLIDE_STEP = 25 — caps new mounts per commit to bound reconcile time.
  • PESSIMISTIC_HEIGHT = 1 for coverage back-walk (guarantees viewport coverage).
  • MAX_MOUNTED_ITEMS = 300 cap.
  • On column change: scales all cached heights by oldCols/newCols instead of clearing.
  • Uses useSyncExternalStore over the ScrollBox's scroll-top external store.
  • Uses useLayoutEffect to measure Yoga heights after each commit.
  • Sticky-scroll: when pinned to the bottom, always renders the last N items.

Dependencies: useRef, useMemo, useDeferredValue, useLayoutEffect, useSyncExternalStore, ScrollBox handle


Permission & Tool-Use Hooks

useCanUseTool

File: hooks/useCanUseTool.tsx

Purpose: Core permission gate for tool execution. Called for every tool use attempt; routes through the appropriate handler (coordinator, interactive, swarm-worker) and resolves to a PermissionDecision.

Parameters:

  • setToolUseConfirmQueue: SetState<ToolUseConfirm[]>
  • setToolPermissionContext: (ctx: ToolPermissionContext) => void

Return Value: CanUseToolFnasync (tool, input, toolUseContext, assistantMessage, toolUseID) => PermissionDecision

Key Logic:

  1. Calls hasPermissionsToUseTool to get the initial decision (allow, deny, or ask).
  2. If allow or deny: logs and returns immediately.
  3. If ask: a. If swarm worker: delegates to handleSwarmWorkerPermission (forwards to leader via mailbox). b. If coordinator worker: delegates to handleCoordinatorPermission (awaits hooks + classifier, then falls through). c. Otherwise: delegates to handleInteractivePermission (shows dialog, races hooks/classifier/bridge/channel).
  4. Creates a PermissionContext object with the full set of callbacks (logDecision, persistPermissions, tryClassifier, runHooks, etc.).

Dependencies: useCallback, useAppState, useSetAppState, createPermissionContext, createPermissionQueueOps, handleCoordinatorPermission, handleInteractivePermission, handleSwarmWorkerPermission


CancelRequestHandler (exported as useCancelRequest module)

File: hooks/useCancelRequest.ts

Purpose: React component (renders null) that registers three keybinding handlers for cancellation:

  1. chat:cancel (Escape) — cancels running task or pops queued command.
  2. app:interrupt (Ctrl+C) — cancels running task; in teammate view, also kills all agents and exits.
  3. chat:killAgents (Ctrl+X Ctrl+K) — two-press pattern to stop all background agents.

Parameters: CancelRequestHandlerProps:

  • setToolUseConfirmQueue, onCancel, onAgentsKilled
  • isMessageSelectorVisible, screen
  • abortSignal?: AbortSignal
  • popCommandFromQueue?, vimMode, isLocalJSXCommand, isSearchingHistory, isHelpOpen
  • inputMode?, inputValue?, streamMode?

Return Value: null

Key Logic:

  • handleCancel: Priority 1 — abort signal if task running. Priority 2 — pop command if queue non-empty. Fallback — call onCancel.
  • handleInterrupt: if in teammate view, kills all agents + exits. Then calls handleCancel.
  • handleKillAgents: first press shows "Press again" hint; second press within 3 000 ms (KILL_AGENTS_CONFIRM_WINDOW_MS) kills all local_agent tasks, emits SDK events, enqueues aggregate notification.
  • isEscapeActive / isCtrlCActive guards: skip if overlay, vim INSERT, transcript, history-search, help, etc.
  • chat:killAgents always registered to prevent Ctrl+K (chord prefix) passing to readline.

Dependencies: useKeybinding, useAppState, useSetAppState, useCommandQueue, useNotifications, useIsOverlayActive, useCallback, useRef, killAllRunningAgentTasks, emitTaskTerminatedSdk


useSwarmPermissionPoller

File: hooks/useSwarmPermissionPoller.ts

Purpose: Polls every 500 ms for permission responses from the swarm leader when running as a worker agent. When a response arrives, it invokes the registered callback (onAllow or onReject).

Parameters: none

Return Value: void

Key Logic:

  • Only active when isSwarmWorker() returns true.
  • Uses useInterval (usehooks-ts) with POLL_INTERVAL_MS = 500.
  • For each requestId in pendingCallbacks, calls pollForResponse(requestId, agentName, teamName).
  • On response: calls processResponse(response) which invokes the callback, then calls removeWorkerResponse.
  • Module-level pendingCallbacks: Map<string, PermissionResponseCallback> and pendingSandboxCallbacks: Map<...>.
  • Exported helper functions: registerPermissionCallback, unregisterPermissionCallback, hasPermissionCallback, clearAllPendingCallbacks, processMailboxPermissionResponse, registerSandboxPermissionCallback, hasSandboxPermissionCallback, processSandboxPermissionResponse.

Dependencies: useCallback, useEffect, useRef, useInterval, permissionSync


Swarm / Teammate Hooks

useSwarmInitialization

File: hooks/useSwarmInitialization.ts

Purpose: Initializes swarm features (teammate context and hooks) on mount. Handles both resumed sessions (teamName/agentName in transcript) and fresh spawns (environment variables).

Parameters:

  • setAppState: SetAppState
  • initialMessages: Message[] | undefined
  • { enabled?: boolean }

Return Value: void

Key Logic:

  • Checks isAgentSwarmsEnabled() before doing anything.
  • Resumed session path: reads teamName/agentName from initialMessages[0], calls initializeTeammateContextFromSession, reads team file to get agentId, calls initializeTeammateHooks.
  • Fresh spawn path: calls getDynamicTeamContext() to read env vars, then calls initializeTeammateHooks.

Dependencies: useEffect


useTeammateViewAutoExit

File: hooks/useTeammateViewAutoExit.ts

Purpose: Auto-exits teammate viewing mode when the viewed teammate is killed, fails, encounters an error, or is evicted from the task map.

Parameters: none

Return Value: void

Key Logic:

  • Selects only viewingAgentTaskId and the viewed task from AppState (avoids re-rendering on unrelated streaming updates).
  • Narrows the task to InProcessTeammateTask type.
  • Exits if task evicted, status is killed, failed, or error is present.
  • Does NOT exit if status is running, completed, or pending.

Dependencies: useEffect, useAppState, useSetAppState, exitTeammateView


useBackgroundTaskNavigation

File: hooks/useBackgroundTaskNavigation.ts

Purpose: Manages keyboard navigation of the background task list. Shift+Up/Down moves selection; Enter enters the view; f opens the full transcript; k kills the task; Escape exits.

Parameters: options?: { isActive?: boolean }

Return Value: { handleKeyDown: (key: Key, input: string) => void }

Key Logic:

  • Reads the list of background tasks from AppState.
  • selectedIndex is clamped to [0, tasks.length - 1] whenever the list changes.
  • Enter: sets viewingAgentTaskId in AppState to the selected task's ID.
  • f: sets showFullTranscript = true in AppState.
  • k: calls killTask(task.id).
  • Escape: calls exitTeammateView(setAppState).

Dependencies: useAppState, useSetAppState, useState, useEffect, useCallback


useInboxPoller

File: hooks/useInboxPoller.ts

Purpose: Polls the team lead's inbox every 1 second (or on demand when idle) and routes messages. Handles permission requests/responses, sandbox permissions, plan approvals, shutdown handling, team permission updates, mode-set requests, and regular messages.

Parameters:

  • { enabled: boolean, isLoading: boolean, focusedInputDialog: string | null, onSubmitMessage: (msg) => void }

Return Value: void

Key Logic:

  • Uses useInterval with 1 000 ms period; no-ops if !enabled.
  • Reads messages from the team lead's mailbox directory.
  • Dispatches by message type:
    • permission_request → adds to toolUseConfirmQueue.
    • permission_response → calls processMailboxPermissionResponse.
    • sandbox_permission_request → calls sandbox permission handler.
    • sandbox_permission_response → calls processSandboxPermissionResponse.
    • plan_approval → routes to plan approval handler.
    • shutdown → gracefully exits.
    • team_permission_update → updates AppState permission context.
    • mode_set → changes permission mode.
    • Regular messages → calls onSubmitMessage when idle.
  • Delivers pending messages only when !isLoading and no focused input dialog.

Dependencies: useInterval, useEffect, useRef, useAppState, useSetAppState, processMailboxPermissionResponse, processSandboxPermissionResponse


useTaskListWatcher

File: hooks/useTaskListWatcher.ts

Purpose: Watches a task list directory and automatically picks up open, unowned tasks to work on (tasks mode). Claims tasks atomically to prevent race conditions.

Parameters:

  • { taskListId?: string, isLoading: boolean, onSubmitTask: (prompt: string) => boolean }

Return Value: void

Key Logic:

  • Calls ensureTasksDir on mount, then watch(tasksDir, debouncedCheck) with DEBOUNCE_MS=1000.
  • Uses stable refs for isLoading and onSubmitTask to avoid Bun PathWatcherManager deadlock (oven-sh/bun#27469) by not recreating the watcher on every turn.
  • checkForTasks: lists tasks, finds status=pending, owner=undefined, all blockedBy completed; calls claimTask(taskListId, task.id, agentId).
  • Formats task as "Complete all open tasks. Start with task #N: ...\n\nDescription".
  • Additional useEffect on isLoading to trigger check when going idle.

Dependencies: fs.watch, useEffect, useRef


useTasksV2

File: hooks/useTasksV2.ts

Purpose: Exposes the current task list for the persistent TodoV2 UI. All consumers share a single TasksV2Store (singleton file-watcher) to avoid watcher churn.

Parameters: none

Return Value: Task[] | undefinedundefined when hidden (all completed for >5 s, or empty).

Key Logic:

  • TasksV2Store class: manages fs.watch, onTasksUpdated subscription, debounced fetch (DEBOUNCE_MS=50), hide timer (HIDE_DELAY_MS=5000), fallback poll (FALLBACK_POLL_MS=5000).
  • getSnapshot returns undefined when #hidden = true.
  • useSyncExternalStore subscription; store starts on first subscriber, stops on last unsubscribe.
  • Only active when isTodoV2Enabled() and (no team context, or is team lead).
  • useTasksV2WithCollapseEffect: same as useTasksV2 plus collapses the expanded task view in AppState when the list becomes hidden.

Dependencies: useSyncExternalStore, useEffect, useAppState, useSetAppState, fs.watch


useSessionBackgrounding

File: hooks/useSessionBackgrounding.ts

Purpose: Manages Ctrl+B backgrounding and foregrounding of the current session. When a task is foregrounded, it syncs that task's messages to the main message list.

Parameters: (large props including setMessages, setIsLoading, tools, etc.)

Return Value: { handleBackgroundSession: () => void }

Key Logic:

  • On handleBackgroundSession: if a task is running, backgrounds it (writes to AppState background tasks); otherwise foregrounds the first background task.
  • Foreground: injects the backgrounded task's messages into the main list via setMessages, resumes the task's streams.

Dependencies: useCallback, useAppState, useSetAppState


useScheduledTasks

File: hooks/useScheduledTasks.ts

Purpose: Mounts the cron scheduler in the REPL. Fired tasks are enqueued via enqueuePendingNotification at later priority; teammate-scoped crons are injected directly into that teammate's message stream.

Parameters:

  • { isLoading: boolean, assistantMode?: boolean, setMessages: Dispatch<SetStateAction<Message[]>> }

Return Value: void

Key Logic:

  • Gated on isKairosCronEnabled() at effect time.
  • Uses isLoadingRef to avoid stale closures on isLoading.
  • onFireTask callback: if task has agentId, finds the teammate and calls injectUserMessageToTeammate; otherwise creates a ScheduledTaskFireMessage and enqueues the prompt.
  • createCronScheduler is the shared scheduler core (used also by print.ts for headless mode).
  • isKilled callback polls isKairosCronEnabled() each tick as a mid-session killswitch.

Dependencies: useEffect, useRef, useAppStateStore, useSetAppState, createCronScheduler


IDE Integration Hooks

useIDEIntegration

File: hooks/useIDEIntegration.tsx

Purpose: Manages IDE auto-connection on startup. Detects running IDEs, sets up dynamic MCP config for the found IDE server, and shows the IDE onboarding dialog if needed.

Parameters:

  • { autoConnectIdeFlag, ideToInstallExtension, setDynamicMcpConfig, setShowIdeOnboarding, setIDEInstallationState }

Return Value: void

Key Logic:

  • Calls detectIDEs() on mount to find running IDE extension servers.
  • If found: calls setDynamicMcpConfig to add the IDE's MCP server config.
  • Checks settings.ideHintShownCount to decide whether to show onboarding.
  • Handles the ideToInstallExtension CLI flag for direct IDE extension install flows.

Dependencies: useEffect, useRef, useAppState


useIdeAtMentioned

File: hooks/useIdeAtMentioned.ts

Purpose: Listens for at_mentioned MCP notifications from the IDE extension and calls a callback with file/line context so Claude can reference the file.

Parameters:

  • mcpClients: MCPServerConnection[]
  • onAtMentioned: (filePath: string, lineNumber?: number) => void

Return Value: void

Key Logic:

  • Uses getConnectedIdeClient(mcpClients) to find the IDE client.
  • Registers a notification handler for the at_mentioned method via ideClient.client.setNotificationHandler.
  • Passes parsed filePath and lineNumber to onAtMentioned.

Dependencies: useEffect


useIdeConnectionStatus

File: hooks/useIdeConnectionStatus.ts

Purpose: Returns the current IDE connection status (connected, disconnected, pending, or null) and the IDE name.

Parameters:

  • mcpClients?: MCPServerConnection[]

Return Value: { status: IDEConnectionStatus | null, ideName: string | null }

Key Logic:

  • Uses useMemo over mcpClients to find the IDE client by checking isIdeClient(client).
  • Maps client connection state to the status enum.

Dependencies: useMemo


useIdeLogging

File: hooks/useIdeLogging.ts

Purpose: Registers a log_event MCP notification handler on the IDE client to forward IDE telemetry events to the analytics system.

Parameters:

  • mcpClients: MCPServerConnection[]

Return Value: void

Key Logic:

  • Calls getConnectedIdeClient to find the IDE client.
  • Registers a Zod-validated handler: { method: 'log_event', params: { eventName, eventData } }.
  • Calls logEvent('tengu_ide_${eventName}', eventData).

Dependencies: useEffect, zod


useIdeSelection

File: hooks/useIdeSelection.ts

Purpose: Listens for selection_changed MCP notifications from the IDE and delivers them as IDESelection objects to the REPL.

Parameters:

  • mcpClients: MCPServerConnection[]
  • onSelect: (selection: IDESelection) => void

Return Value: void

Key Logic:

  • Finds IDE client, registers notification handler for selection_changed.
  • Converts the raw notification payload to IDESelection format { filePath, text, lineStart, lineEnd, lineCount }.

Dependencies: useEffect


useDiffInIDE

File: hooks/useDiffInIDE.ts

Purpose: Opens a file diff in the connected IDE via MCP RPC. Handles user save/close/reject responses to finalize or revert the edit.

Parameters:

  • { onChange, toolUseContext, filePath, edits, editMode }

Return Value: { closeTabInIDE, showingDiffInIDE, ideName, hasError }

Key Logic:

  • Calls ideClient.client.request('show_diff', { filePath, edits, ... }) to open the diff in the IDE.
  • Waits for the IDE to respond with saved, closed, or rejected.
  • On saved: calls onChange to apply the edit.
  • On rejected: calls the abort controller.
  • Returns closeTabInIDE() so the permission dialog can close the tab programmatically.

Dependencies: useState, useRef, useCallback, useEffect


Remote & Session Hooks

useDirectConnect

File: hooks/useDirectConnect.ts

Purpose: Manages a WebSocket connection to a DirectConnect server (local server mode). Routes inbound messages and permission requests between the server and the REPL.

Parameters:

  • { config, setMessages, setIsLoading, setToolUseConfirmQueue, tools }

Return Value: UseDirectConnectResult{ isConnected, send }

Key Logic:

  • Uses directConnectManager to manage the WebSocket lifecycle.
  • Translates inbound SDK messages to Message[] format.
  • Handles tool permission requests by pushing to setToolUseConfirmQueue.
  • Reconnects on disconnect with exponential backoff.

Dependencies: useEffect, useRef, useState


useSSHSession

File: hooks/useSSHSession.ts

Purpose: Wires an SSH session manager to the REPL. Handles reconnection, graceful shutdown on disconnect, and transcript message injection.

Parameters:

  • { session, setMessages, setIsLoading, setToolUseConfirmQueue, tools }

Return Value: UseSSHSessionResult{ isConnected, disconnect }

Key Logic:

  • Subscribes to SSH session events: message, connect, disconnect, error.
  • On disconnect: injects a system message explaining the disconnect and showing reconnect options.
  • On reconnect: injects a system message confirming reconnection.
  • Handles graceful shutdown: drains pending messages before closing.

Dependencies: useEffect, useRef, useState


useRemoteSession

File: hooks/useRemoteSession.ts

Purpose: Full CCR (Claude Code Remote) WebSocket session management. Handles bidirectional message conversion, streaming tool uses, permission request/response flow, response timeout detection, session title updates, and subagent task counting.

Parameters: Large props object including:

  • config: AppConfig
  • setMessages: SetMessages
  • setIsLoading, setToolUseConfirmQueue
  • tools: Tool[]
  • onSessionTitleUpdate?: (title: string) => void

Return Value: UseRemoteSessionResult{ isConnected, sendMessage, sessionId, ... }

Key Logic:

  • Connects to CCR WebSocket on mount, reconnects on disconnect.
  • Converts SDKMessage types to internal Message format on inbound.
  • Converts outbound messages to SDK format for CCR consumption.
  • Manages streaming tool uses: accumulates input_json_delta chunks, fires permission dialog on tool_use completion.
  • Response timeout: sets a flag after 30 s of no response from the model.
  • Session title: subscribes to session_title_update events and calls onSessionTitleUpdate.
  • Subagent task counting: tracks subagent_start / subagent_end events to count running agents.

Dependencies: useEffect, useState, useRef, useCallback, SessionsWebSocket


useAssistantHistory

File: hooks/useAssistantHistory.ts

Purpose: Lazy-loads older messages from a remote session's history as the user scrolls up in viewer-only mode.

Parameters:

  • { config, setMessages, scrollRef, onPrepend }

Return Value: { maybeLoadOlder: () => Promise<void> }

Key Logic:

  • On scroll-up event, calls loadSessionHistory(sessionId, pageToken).
  • Prepends loaded messages to the message list.
  • Chains viewport fill: if the loaded messages don't fill the viewport, immediately loads another page.
  • Scroll anchoring: saves the current scroll position before prepending and restores it after.
  • Shows a sentinel message at the top ("Beginning of conversation") once all pages are exhausted.

Dependencies: useCallback, useRef, useEffect


useMailboxBridge

File: hooks/useMailboxBridge.ts

Purpose: Bridges the mailbox message context to the REPL's submit function. Polls the mailbox on revision change when idle.

Parameters:

  • { isLoading: boolean, onSubmitMessage: (msg) => void }

Return Value: void

Key Logic:

  • Subscribes to mailboxRevision changes in AppState.
  • When revision bumps and !isLoading: calls pollMailbox() and submits any pending messages via onSubmitMessage.

Dependencies: useEffect, useRef, useAppState


useReplBridge

File: hooks/useReplBridge.tsx

Purpose: Full REPL bridge session management — the main hook wiring the REPL to the Claude API. Manages the full query execution loop, permission flow, streaming message assembly, compact operations, and bridge connectivity.

Parameters: Large props including tools, messages, setMessages, config, and many callbacks.

Return Value: Large result including { onQuery, isLoading, abortController, toolUseConfirmQueue, ... }

Key Logic: (file is >75k tokens; key points from reading the first 80 lines and the summary)

  • Manages abortController lifecycle — creates a new one per query, aborts on cancel.
  • Calls the streaming Claude API via query.ts.
  • Assembles streaming AssistantMessage from delta events.
  • Routes tool_use to canUseTool for permission gating.
  • Handles compact boundary detection and auto-compact triggers.
  • Integrates bridge callbacks for CCR permission relaying.
  • Manages local session history recording.

Dependencies: useState, useRef, useCallback, useEffect, useMemo, useCanUseTool, useLogMessages, query, compact


useTeleportResume

File: hooks/useTeleportResume.tsx

Purpose: Manages the async lifecycle of teleporting into a remote Code Session: loading state, error state, selected session tracking, and the resumeSession callback.

Parameters:

  • source: TeleportSource'cliArg' | 'localCommand' (for analytics).

Return Value: { resumeSession, isResuming, error, selectedSession, clearError }

Key Logic:

  • resumeSession(session): sets isResuming = true, logs tengu_teleport_resume_session, calls teleportResumeCodeSession(session.id), sets teleportedSessionInfo for reliability logging.
  • On error: wraps in TeleportResumeError with isOperationError flag for UI differentiation.
  • Uses React Compiler (_c) memoization.

Dependencies: useState, useCallback, teleportResumeCodeSession, setTeleportedSessionInfo


Plugin & Suggestion Hooks

usePromptSuggestion

File: hooks/usePromptSuggestion.ts

Purpose: Manages AI prompt completion suggestions: fetches a suggestion for the current input, tracks accept/ignore/submit outcomes, and logs telemetry.

Parameters:

  • { inputValue: string, isAssistantResponding: boolean }

Return Value: { suggestion: string | null, markAccepted, markShown, logOutcomeAtSubmission }

Key Logic:

  • Calls generatePromptSuggestion(inputValue) debounced after 300 ms of no typing.
  • markAccepted(): records that the user pressed Tab to accept.
  • markShown(): records when the ghost-text suggestion becomes visible.
  • logOutcomeAtSubmission(): called on submit; logs accept (Tab was pressed), ignore (shown but not accepted), or no_suggestion outcome.

Dependencies: useState, useRef, useCallback, useEffect, generatePromptSuggestion


usePromptsFromClaudeInChrome

File: hooks/usePromptsFromClaudeInChrome.tsx

Purpose: Listens for prompts sent from the Claude in Chrome extension via MCP notifications. Also syncs the current permission mode to the extension.

Parameters:

  • mcpClients: MCPServerConnection[]
  • toolPermissionMode: PermissionMode

Return Value: void

Key Logic:

  • Finds the Chrome extension MCP client.
  • Registers a notification handler for prompt_from_chrome.
  • Submits received prompts to the command queue.
  • On toolPermissionMode change: sends a mode_changed notification back to the extension.

Dependencies: useEffect, useRef


usePrStatus

File: hooks/usePrStatus.ts

Purpose: Polls gh pr status every 60 seconds to detect review state changes on the current branch's PR.

Parameters:

  • isLoading: boolean
  • enabled?: boolean

Return Value: PrStatusState{ reviewStatus: 'approved'|'changes_requested'|'pending'|null, prUrl: string | null }

Key Logic:

  • Uses useInterval with 60 000 ms period; skips poll when isLoading.
  • Stops polling after 60 minutes of idle time (no new turns).
  • Permanently disables if a fetch takes >4 seconds (likely no gh binary or no PR).
  • Runs gh pr status --json reviewDecision,url in a subprocess.

Dependencies: useInterval, useState, useRef


useClaudeCodeHintRecommendation

File: hooks/useClaudeCodeHintRecommendation.tsx

Purpose: Surfaces plugin install prompts from <claude-code-hint /> tags parsed from Claude's responses. Show-once semantics per plugin per session.

Parameters: none

Return Value: { recommendation: PluginRecommendation | null, handleResponse: (accepted: boolean) => void }

Key Logic:

  • Monitors messages for assistant messages containing <claude-code-hint plugin="name" /> XML.
  • Uses usePluginRecommendationBase state machine to gate display.
  • handleResponse(true): installs the plugin; false: dismisses.

Dependencies: usePluginRecommendationBase, useAppState, useEffect


useLspPluginRecommendation

File: hooks/useLspPluginRecommendation.tsx

Purpose: Recommends an LSP plugin when the user edits a file whose extension matches a supported language and the LSP binary is present.

Parameters: none

Return Value: { recommendation: PluginRecommendation | null, handleResponse: (accepted: boolean) => void }

Key Logic:

  • Watches messages for FileEdit / FileWrite tool result messages.
  • Extracts the file extension, checks if there's a matching LSP plugin.
  • Uses usePluginRecommendationBase to avoid showing while another recommendation is active.
  • Show-once per session (tracked in AppState).

Dependencies: usePluginRecommendationBase, useAppState, useEffect


usePluginRecommendationBase

File: hooks/usePluginRecommendationBase.tsx

Purpose: Shared state machine for plugin recommendations. Guards against showing a recommendation while in remote mode, while another is already showing, or while a check is in-flight.

Parameters: generic T

Return Value: { recommendation: T | null, clearRecommendation, tryResolve: (candidate: T | null) => void }

Key Logic:

  • tryResolve(candidate): if remote mode or already showing, no-op; otherwise sets recommendation.
  • clearRecommendation(): clears the current recommendation.
  • Guards inFlight ref to prevent concurrent checks.

Dependencies: useState, useRef, useCallback


useOfficialMarketplaceNotification

File: hooks/useOfficialMarketplaceNotification.tsx

Purpose: Handles official marketplace auto-install on first launch and shows success/failure startup notifications.

Parameters: none

Return Value: void

Key Logic:

  • On mount: checks settings.autoInstallOfficialMarketplace flag.
  • If set and not yet installed: calls installOfficialMarketplace().
  • Shows a startup priority notification on success or failure.

Dependencies: useEffect, useRef, useNotifications


useChromeExtensionNotification

File: hooks/useChromeExtensionNotification.tsx

Purpose: Shows startup notifications about the Chrome extension status: requires subscription, not installed, or default-enabled.

Parameters: none

Return Value: void

Key Logic:

  • Uses useStartupNotification (notifs/) internally.
  • Checks chrome_extension_status from settings/config.
  • Returns appropriate notification text for each status.

Dependencies: useStartupNotification


useClipboardImageHint

File: hooks/useClipboardImageHint.ts

Purpose: Shows a notification when the terminal gains focus and the clipboard contains an image. Debounced with 1 000 ms delay and a 30-second cooldown.

Parameters:

  • isFocused: boolean
  • enabled: boolean

Return Value: void

Key Logic:

  • Watches isFocused changes.
  • On focus gain: checks clipboard via navigator.clipboard.read() for image/* MIME.
  • If image found: calls addNotification({ key: 'clipboard-image', text: 'Image in clipboard · Ctrl+V to attach' }).
  • Cooldown: stores last-shown timestamp to avoid notification spam.

Dependencies: useEffect, useRef, useNotifications


useManagePlugins

File: hooks/useManagePlugins.ts — (same as above; see Core section for full details)


useIssueFlagBanner

File: hooks/useIssueFlagBanner.ts

Purpose: ANT-internal: shows an issue-flag banner after a session has friction signals (e.g. repeated tool retries) and has been active for at least 30 minutes with 3+ submits.

Parameters:

  • messages: readonly Message[]
  • submitCount: number

Return Value: boolean — whether to show the banner.

Key Logic:

  • Counts tool errors, retries, and refusals in recent messages.
  • Only enabled for ANT internal users (checks isAntInternal()).
  • 30-minute cooldown stored in localStorage.

Dependencies: useMemo, useRef


useQueueProcessor

File: hooks/useQueueProcessor.ts

Purpose: Processes queued commands from the unified command queue when no active query is running and no blocking UI is shown.

Parameters:

  • { executeQueuedInput: (cmd: QueuedCommand) => void, hasActiveLocalJsxUI: boolean, queryGuard: () => boolean }

Return Value: void

Key Logic:

  • Uses two useSyncExternalStore subscriptions: one for the command queue, one for the "is loading" state.
  • When the queue is non-empty, !isLoading, !hasActiveLocalJsxUI, and queryGuard() returns true: dequeues and executes the next command.
  • Uses useEffect to trigger processing on state changes.

Dependencies: useSyncExternalStore, useEffect, messageQueueManager


useTurnDiffs

File: hooks/useTurnDiffs.ts

Purpose: Extracts per-turn file diffs from the message list for display in the /diff view. Uses incremental processing — only new messages are scanned on each render.

Parameters:

  • messages: Message[]

Return Value: TurnDiff[] — reverse-chronological list of turns that modified files.

Key Logic:

  • TurnDiff: { turnIndex, userPromptPreview, timestamp, files: Map<string, TurnFileDiff>, stats }.
  • Detects turn boundaries from user messages that are not tool results and not isMeta.
  • Collects FileEdit/FileWrite tool results within each turn.
  • New-file hunks: generates synthetic +line hunks from content.
  • Accumulated across edits to the same file in a turn.
  • Cache ref holds completedTurns + currentTurn + lastProcessedIndex for O(n_new) processing.

Dependencies: useMemo, useRef


useSkillImprovementSurvey

File: hooks/useSkillImprovementSurvey.ts

Purpose: Manages the skill improvement survey dialog. Triggered by AppState.skillImprovementSurvey, applies improvements to the skills file on accept.

Parameters:

  • setMessages: SetMessages

Return Value: { isOpen: boolean, suggestion: SkillSuggestion | null, handleSelect: (selection) => void }

Key Logic:

  • Reads AppState.skillImprovementSurvey to get the pending survey.
  • handleSelect('apply'): calls applySkillImprovement(suggestion) and injects a system message.
  • handleSelect('dismiss'): clears the survey from AppState.
  • On any selection: clears AppState.skillImprovementSurvey.

Dependencies: useAppState, useSetAppState, useCallback


useGlobalKeybindings

File: hooks/useGlobalKeybindings.tsx

Purpose: React component (renders null) that registers global keybinding handlers for Ctrl+T (toggle todos), Ctrl+O (toggle transcript/messages), Ctrl+E (toggle show-all), and Escape/Ctrl+C (exit transcript).

Parameters: Props including screen, isLoading, isSearchingHistory, isHelpOpen, etc.

Return Value: null

Key Logic:

  • Registers view:toggleTasks, view:toggleTranscript, view:toggleShowAll bindings.
  • KAIROS feature flag gates: view:toggleTasks only active when isTodoV2Enabled().
  • view:toggleTranscript sets expandedView = 'messages' or 'none' in AppState.
  • view:toggleShowAll sets showAllMessages in AppState.

Dependencies: useKeybinding, useAppState, useSetAppState


CommandKeybindingHandlers

File: hooks/useCommandKeybindings.tsx

Purpose: Registers all command:* keybinding actions as slash command submitters — e.g., command:compact, command:memory, command:config, etc.

Parameters: { onSubmit: (cmd: string) => void, isActive?: boolean }

Return Value: null

Key Logic:

  • Calls useKeybindings with a map of command:X() => onSubmit('/X') entries.
  • Derives the slash command name from the keybinding action name.

Dependencies: useKeybindings


useAwaySummary

File: hooks/useAwaySummary.ts

Purpose: Appends a "while you were away" summary message after the terminal has been blurred for 5 minutes, when no turn is in progress.

Parameters:

  • messages: readonly Message[]
  • setMessages: SetMessages
  • isLoading: boolean

Return Value: void

Key Logic:

  • Gated on feature('AWAY_SUMMARY') bundle flag and tengu_sedge_lantern GrowthBook flag.
  • Subscribes to subscribeTerminalFocus for blur/focus events.
  • On blur: starts a BLUR_DELAY_MS = 5 * 60_000 timer.
  • Timer fire: if still loading, sets pendingRef = true (deferred); otherwise calls generate().
  • generate(): calls generateAwaySummary(messages, signal) → appends createAwaySummaryMessage(text).
  • On focus: clears timer, aborts in-flight generation, clears pendingRef.
  • Second useEffect on isLoading: if !isLoading and pendingRef and still blurred, fires generate().
  • hasSummarySinceLastUserTurn(): walks backward to prevent duplicate summaries.

Dependencies: useEffect, useRef, useCallback, subscribeTerminalFocus, generateAwaySummary


useAfterFirstRender / renderPlaceholder

See entry under "Core / Utility Hooks" for useAfterFirstRender.


Notification Hooks (notifs/)

All hooks in notifs/ use useNotifications() from context/notifications.js to push entries to the status bar notification queue. Most are gated on !getIsRemoteMode().


useStartupNotification

File: hooks/notifs/useStartupNotification.ts

Purpose: Base primitive for fire-once-on-mount notifications. Encapsulates the remote-mode gate and once-per-session ref guard used by most other notifs/ hooks.

Parameters:

  • compute: () => Result | Promise<Result> — returns null to skip, Notification for one, Notification[] for many.

Return Value: void

Key Logic:

  • hasRunRef prevents re-firing on re-render.
  • Runs compute inside Promise.resolve().then(...) to allow async.
  • Catches errors via logError.
  • Skips entirely in remote mode.

Dependencies: useEffect, useRef, useNotifications


useAutoModeUnavailableNotification

File: hooks/notifs/useAutoModeUnavailableNotification.ts

Purpose: Shows a one-shot warning when the Shift+Tab mode carousel wraps past where "auto mode" would have been, explaining why auto mode is unavailable.

Parameters: none

Key Logic:

  • Detects the wrap: mode === 'default' && prevMode !== 'default' && prevMode !== 'auto' && !isAutoModeAvailable && hasAutoModeOptIn().
  • Calls getAutoModeUnavailableReason() to get the specific reason (circuit-breaker, org-allowlist, settings).
  • shownRef prevents showing more than once per session.
  • Gated on feature('TRANSCRIPT_CLASSIFIER').

useCanSwitchToExistingSubscription

File: hooks/notifs/useCanSwitchToExistingSubscription.tsx

Purpose: Shows up to 3 times (MAX_SHOW_COUNT=3) across sessions a notification prompting users who have a Claude Pro/Max subscription but are logged in via API key to run /login.

Key Logic:

  • Reads globalConfig.subscriptionNoticeCount; returns null if >= 3.
  • Calls getOauthProfileFromApiKey() to check for Pro/Max subscription.
  • Increments subscriptionNoticeCount in global config on each show.
  • Renders a JSX notification with color="suggestion".

useDeprecationWarningNotification

File: hooks/notifs/useDeprecationWarningNotification.tsx

Purpose: Shows a color="warning" notification when the active model is deprecated.

Parameters: model: string

Key Logic:

  • Calls getModelDeprecationWarning(model) on each model change.
  • Uses lastWarningRef to avoid re-adding the same notification on re-render.
  • Resets tracking if model changes to non-deprecated.

useFastModeNotification

File: hooks/notifs/useFastModeNotification.tsx

Purpose: Shows real-time notifications for fast mode state changes: cooldown started/expired, org-level enable/disable, and overage rejection.

Key Logic:

  • Subscribes to onCooldownTriggered, onCooldownExpired, onFastModeOverageRejection, onOrgFastModeChanged event emitters.
  • On org-disabled while fast mode is active: disables fast mode in AppState.
  • Shows immediate-priority notifications with color="fastMode" or color="warning".

useIDEStatusIndicator

File: hooks/notifs/useIDEStatusIndicator.tsx

Purpose: Shows IDE connection status in the notification area: hint to install extension, JetBrains info, install error, or current selection preview.

Parameters: { ideInstallationStatus, ideSelection, mcpClients }

Key Logic:

  • Uses useIdeConnectionStatus(mcpClients) to get current status.
  • Shows "install extension" hint up to MAX_IDE_HINT_SHOW_COUNT=5 times (tracked in globalConfig).
  • Shows JetBrains info notification (different flow than VS Code).
  • Shows selection preview as a persistent notification when file/text is selected.

useInstallMessages

File: hooks/notifs/useInstallMessages.tsx

Purpose: Shows startup notifications for native installer issues (PATH not configured, alias not set, install errors).

Key Logic:

  • Calls checkInstall() to get installation messages.
  • Maps message types to priorities: error/userActionRequiredhigh; path/aliasmedium; others → low.
  • Colors: errorcolor="error"; others → color="warning".

useLspInitializationNotification

File: hooks/notifs/useLspInitializationNotification.tsx

Purpose: Polls LSP server status every 5 000 ms and shows notifications when the LSP manager or individual servers fail to initialize.

Key Logic:

  • Gated on ENABLE_LSP_TOOL env var.
  • Polls getInitializationStatus() and getLspServerManager().
  • De-duplicates errors using notifiedErrorsRef set.
  • Adds errors to appState.plugins.errors for /doctor display.

useMcpConnectivityStatus

File: hooks/notifs/useMcpConnectivityStatus.tsx

Purpose: Shows notifications when MCP servers fail to connect or need authentication.

Parameters: { mcpClients?: MCPServerConnection[] }

Key Logic:

  • Filters mcpClients by connection state: failed, needs_auth.
  • Separately tracks claudeai clients (connector) vs local clients (server).
  • Shows JSX notifications with counts and · /mcp navigation hint.

useModelMigrationNotifications

File: hooks/notifs/useModelMigrationNotifications.tsx

Purpose: Shows one-time notifications immediately after automatic model migrations (e.g. Sonnet 4.5 → 4.6, Opus Pro → Opus 4.6).

Key Logic:

  • Uses useStartupNotification with a MIGRATIONS array of check functions.
  • Each check reads a timestamp field from globalConfig and returns a notification if the timestamp is within the last 3 seconds (i.e., this is the launch that triggered the migration).

useNpmDeprecationNotification

File: hooks/notifs/useNpmDeprecationNotification.tsx

Purpose: Shows a 15-second warning notification when Claude Code is running via an npm install (deprecated) rather than the native installer.

Key Logic:

  • Skips if isInBundledMode() or DISABLE_INSTALLATION_CHECKS env var is set.
  • Calls getCurrentInstallationType(); skips for 'development' installs.

usePluginAutoupdateNotification

File: hooks/notifs/usePluginAutoupdateNotification.tsx

Purpose: Subscribes to onPluginsAutoUpdated and shows a notification prompting the user to run /reload-plugins when plugins have been auto-updated in the background.

Key Logic:

  • useState([]) for updatedPlugins list.
  • onPluginsAutoUpdated subscription fires with updated plugin IDs.
  • Extracts plugin names (strips @marketplace suffix) and shows JSX notification with color="success".
  • 10 000 ms timeout.

usePluginInstallationStatus

File: hooks/notifs/usePluginInstallationStatus.tsx

Purpose: Shows a notification when one or more plugins fail to install (from AppState plugins.installationStatus).

Key Logic:

  • Reads installationStatus.marketplaces and installationStatus.plugins from AppState.
  • Filters by status === 'failed', memoizes counts.
  • Shows "N plugins failed to install · /plugin for details" with priority: 'medium'.

useRateLimitWarningNotification

File: hooks/notifs/useRateLimitWarningNotification.tsx

Purpose: Shows rate limit warnings: (1) immediate notification when entering overage mode; (2) warning notification when approaching usage limits.

Parameters: model: string

Key Logic:

  • useClaudeAiLimits() for reactive limit data.
  • getRateLimitWarning(limits, model) — string describing approaching limit.
  • getUsingOverageText(limits) — string for overage mode.
  • Overage notification shown once per overage entry (tracked via hasShownOverageNotification state).
  • Team/enterprise: skips overage notification unless user has billing access.
  • Warning notification shown only when text changes (deduped via shownWarningRef).

useSettingsErrors

File: hooks/notifs/useSettingsErrors.tsx

Purpose: Watches for settings validation errors (from getSettingsWithAllErrors) and shows/removes a warning notification in the status bar.

Return Value: ValidationError[] — the current list of errors (also used for /doctor display).

Key Logic:

  • Initial state populated synchronously from getSettingsWithAllErrors().
  • useSettingsChange subscription: re-reads errors on file change.
  • Shows "Found N settings issues · /doctor for details" with 60 000 ms timeout.
  • Removes notification when errors clear.

useTeammateShutdownNotification / useTeammateLifecycleNotification

File: hooks/notifs/useTeammateShutdownNotification.ts

Purpose: Fires batched spawn/shutdown notifications when in-process teammates start or complete. Uses fold() to combine "1 agent spawned" + "1 agent spawned" into "2 agents spawned".

Key Logic:

  • Reads tasks from AppState.
  • Tracks seen running/completed IDs in seenRunningRef / seenCompletedRef.
  • makeSpawnNotif(count) / makeShutdownNotif(count) with 5 000 ms timeout and fold function.
  • Exported as useTeammateLifecycleNotification.

Tool Permission Subsystem (toolPermission/)

PermissionContext.ts

File: hooks/toolPermission/PermissionContext.ts

Purpose: Factory for creating a PermissionContext object — the shared context passed to all three permission handlers. Contains all callbacks needed to approve, deny, log, queue, and persist permission decisions.

Exported Functions:

  • createPermissionContext(tool, input, toolUseContext, assistantMessage, toolUseID, setToolPermissionContext, queueOps?) → PermissionContext
  • createPermissionQueueOps(setToolUseConfirmQueue) → PermissionQueueOps — bridges React state setter to the generic queue interface.
  • createResolveOnce<T>(resolve) → ResolveOnce<T> — atomic check-and-mark-as-resolved guard for races.

PermissionContext methods:

  • logDecision(args, opts?) — delegates to logPermissionDecision.
  • logCancelled() — logs tengu_tool_use_cancelled event.
  • persistPermissions(updates) — calls persistPermissionUpdates and updates AppState.
  • resolveIfAborted(resolve) — short-circuits if abort signal is fired.
  • cancelAndAbort(feedback?, isAbort?, contentBlocks?) — builds a deny decision and aborts the controller if appropriate.
  • tryClassifier(pendingCheck, updatedInput) — awaits classifier auto-approval (bash only; BASH_CLASSIFIER feature flag).
  • runHooks(permissionMode, suggestions, updatedInput?, startTimeMs?) — executes PermissionRequest hooks sequentially.
  • buildAllow(updatedInput, opts?)PermissionAllowDecision
  • buildDeny(message, reason)PermissionDenyDecision
  • handleUserAllow(updatedInput, permissionUpdates, feedback?, startTimeMs?, contentBlocks?, decisionReason?) — persists updates, logs, returns allow decision.
  • handleHookAllow(finalInput, permissionUpdates, startTimeMs?) — same for hook-sourced allows.
  • pushToQueue(item), removeFromQueue(), updateQueueItem(patch) — queue management via queueOps.

permissionLogging.ts

File: hooks/toolPermission/permissionLogging.ts

Purpose: Centralized analytics and telemetry logging for all tool permission decisions. Fans out to Statsig (logEvent), OTel telemetry, code-edit metrics, and the toolUseContext.toolDecisions map.

Exported Functions:

  • logPermissionDecision(ctx, args, startTimeMs?) — main entry point.
  • isCodeEditingTool(toolName) — checks if tool is Edit/Write/NotebookEdit.
  • buildCodeEditToolAttributes(tool, input, decision, source) — builds OTel attributes including language from file path.

Analytics Events:

  • tengu_tool_use_granted_in_config — auto-approved by settings allowlist.
  • tengu_tool_use_granted_in_prompt_permanent / _temporary — user approved.
  • tengu_tool_use_granted_by_permission_hook — hook approved.
  • tengu_tool_use_granted_by_classifier — classifier approved.
  • tengu_tool_use_rejected_in_prompt — any rejection.
  • tengu_tool_use_denied_in_config — denied by settings denylist.

handlers/coordinatorHandler.ts

File: hooks/toolPermission/handlers/coordinatorHandler.ts

Purpose: Handles the coordinator-worker permission flow: runs hooks then classifier (both awaited sequentially) before falling through to the interactive dialog.

Exported: handleCoordinatorPermission(params) → Promise<PermissionDecision | null>

Parameters: CoordinatorPermissionParams{ ctx, pendingClassifierCheck?, updatedInput, suggestions, permissionMode }

Logic:

  1. await ctx.runHooks(...) — if hooks return a decision, return it.
  2. If BASH_CLASSIFIER flag: await ctx.tryClassifier?.(...) — if classifier returns, return it.
  3. Return null → caller falls through to handleInteractivePermission.
  4. On unexpected error: logs and returns null (graceful fallback to dialog).

handlers/interactiveHandler.ts

File: hooks/toolPermission/handlers/interactiveHandler.ts

Purpose: Handles the interactive (main-agent) permission flow. Sets up the ToolUseConfirm queue entry with all callbacks and races user interaction against background automated checks (hooks, classifier, bridge, channel).

Exported: handleInteractivePermission(params, resolve) → void (synchronous setup)

Key Logic:

  • Creates a PermissionConfirm queue entry with callbacks: onAbort, onAllow, onReject, recheckPermission, onUserInteraction, onDismissCheckmark.
  • createResolveOnce guard ensures only the first resolution wins.
  • userInteracted flag: prevents classifier from auto-approving after user interaction (200 ms grace period).
  • Race 1 — User: onAllow/onReject/onAbort callbacks.
  • Race 2 — Hooks: async ctx.runHooks(...) — if win, removes from queue, resolves.
  • Race 3 — Classifier: executeAsyncClassifierCheck(...) — on allow, shows checkmark UI for 3 s (focused) or 1 s (blurred), then removes from queue.
  • Race 4 — Bridge (CCR): sends permission_request to CCR; subscribes to CCR response; on win, logs, resolves.
  • Race 5 — Channel: sends structured permission_request to all active channel MCP servers (Telegram, iMessage); subscribes to response; on win, resolves.
  • Checkmark dismissal: onDismissCheckmark allows user to press Escape during the checkmark window.
  • Abort: if abort signal fires mid-dialog, claim() races to resolve with cancel.

handlers/swarmWorkerHandler.ts

File: hooks/toolPermission/handlers/swarmWorkerHandler.ts

Purpose: Handles the swarm-worker permission flow: tries classifier auto-approval, then forwards the request to the team leader via mailbox. Awaits the leader's response.

Exported: handleSwarmWorkerPermission(params) → Promise<PermissionDecision | null>

Logic:

  1. Returns null if not isAgentSwarmsEnabled() or not isSwarmWorker().
  2. If BASH_CLASSIFIER: tries ctx.tryClassifier?.(...).
  3. Creates a Promise<PermissionDecision> that resolves when the leader responds.
  4. Registers onAllow/onReject callbacks via registerPermissionCallback.
  5. Calls sendPermissionRequestViaMailbox(request) to notify the leader.
  6. Sets AppState.pendingWorkerRequest for visual indicator.
  7. On abort: resolves with cancelAndAbort.
  8. On error: returns null (fallback to local UI handling).

Non-Hook Utilities in hooks/

These files live in hooks/ but are not React hooks.

fileSuggestions.ts

File: hooks/fileSuggestions.ts

Exports:

  • generateFileSuggestions(query, cwd, ...) → Promise<SuggestionItem[]> — main entry point. Manages a FileIndex singleton (native Rust/nucleo), fetches tracked files via git ls-files, falls back to ripgrep. Returns up to 15 scored matches.
  • startBackgroundCacheRefresh(cwd) — queues a background refresh of untracked files.
  • clearFileSuggestionCaches() — called on /clear to reset the index.
  • applyFileSuggestion(suggestion, inputValue, cursorOffset) → string — replaces the @token in the input with the chosen file path.
  • findLongestCommonPrefix(suggestions) → string — used for Tab-autocomplete of common prefix.
  • onIndexBuildComplete(callback) — notifies when background index is built.

Key Design:

  • Path signature (mtime + size) invalidates cache without full rebuild.
  • .ignore / .rgignore file support.
  • Directory name extraction for @dir/ completions.

unifiedSuggestions.ts

File: hooks/unifiedSuggestions.ts

Exports:

  • generateUnifiedSuggestions(query, mcpResources, agents, showOnEmpty) → Promise<SuggestionItem[]> — merges file suggestions (nucleo), MCP resource suggestions (Fuse.js), and agent suggestions into a ranked list of up to 15 items.

Key Design:

  • File suggestions use nucleo score (01 float).
  • MCP resource suggestions use Fuse.js score (lower = better, inverted for sorting).
  • Agent suggestions always appended at lower priority.

renderPlaceholder.ts

File: hooks/renderPlaceholder.ts

Exports:

  • renderPlaceholder(placeholder, hidePlaceholderText?, cursorChar?) → string — pure function that renders placeholder text with a cursor character appended. When hidePlaceholderText = true (voice recording mode), returns only the cursor.