feat: 大规模清理 claude 的类型问题及依赖

This commit is contained in:
claude-code-best
2026-03-31 22:21:35 +08:00
parent 2c759fe6fa
commit 4c0a655a1c
38 changed files with 1154 additions and 718 deletions

View File

@@ -1482,8 +1482,8 @@ async function prepareIfConditionMatcher(
return undefined
}
const toolName = normalizeLegacyToolName(hookInput.tool_name)
const tool = tools && findToolByName(tools, hookInput.tool_name)
const toolName = normalizeLegacyToolName(hookInput.tool_name as string)
const tool = tools && findToolByName(tools, hookInput.tool_name as string)
const input = tool?.inputSchema.safeParse(hookInput.tool_input)
const patternMatcher =
input?.success && tool?.preparePermissionMatcher
@@ -1701,51 +1701,51 @@ export async function getMatchingHooks(
case 'PostToolUseFailure':
case 'PermissionRequest':
case 'PermissionDenied':
matchQuery = hookInput.tool_name
matchQuery = hookInput.tool_name as string
break
case 'SessionStart':
matchQuery = hookInput.source
matchQuery = hookInput.source as string
break
case 'Setup':
matchQuery = hookInput.trigger
matchQuery = hookInput.trigger as string
break
case 'PreCompact':
case 'PostCompact':
matchQuery = hookInput.trigger
matchQuery = hookInput.trigger as string
break
case 'Notification':
matchQuery = hookInput.notification_type
matchQuery = hookInput.notification_type as string
break
case 'SessionEnd':
matchQuery = hookInput.reason
matchQuery = hookInput.reason as string
break
case 'StopFailure':
matchQuery = hookInput.error
matchQuery = hookInput.error as string
break
case 'SubagentStart':
matchQuery = hookInput.agent_type
matchQuery = hookInput.agent_type as string
break
case 'SubagentStop':
matchQuery = hookInput.agent_type
matchQuery = hookInput.agent_type as string
break
case 'TeammateIdle':
case 'TaskCreated':
case 'TaskCompleted':
break
case 'Elicitation':
matchQuery = hookInput.mcp_server_name
matchQuery = hookInput.mcp_server_name as string
break
case 'ElicitationResult':
matchQuery = hookInput.mcp_server_name
matchQuery = hookInput.mcp_server_name as string
break
case 'ConfigChange':
matchQuery = hookInput.source
matchQuery = hookInput.source as string
break
case 'InstructionsLoaded':
matchQuery = hookInput.load_reason
matchQuery = hookInput.load_reason as string
break
case 'FileChanged':
matchQuery = basename(hookInput.file_path)
matchQuery = basename(hookInput.file_path as string)
break
default:
break
@@ -2291,7 +2291,7 @@ async function* executeHooks({
hookName,
toolUseID,
hookEvent,
content: `Failed to prepare hook input: ${errorMessage(jsonInputRes.error)}`,
content: `Failed to prepare hook input: ${errorMessage((jsonInputRes as { ok: false; error: unknown }).error)}`,
command: hookCommand,
durationMs: Date.now() - hookStartMs,
}),
@@ -2637,9 +2637,10 @@ async function* executeHooks({
})
// Handle suppressOutput (skip for async responses)
const syncJson = json as TypedSyncHookOutput
if (
isSyncHookJSONOutput(json) &&
!json.suppressOutput &&
!syncJson.suppressOutput &&
plainText &&
result.status === 0
) {
@@ -3196,14 +3197,15 @@ async function executeHooksOutsideREPL({
}
}
const typedJson = json as TypedSyncHookOutput
const output =
hookEvent === 'WorktreeCreate' &&
isSyncHookJSONOutput(json) &&
json.hookSpecificOutput?.hookEventName === 'WorktreeCreate'
? json.hookSpecificOutput.worktreePath
: json.systemMessage || ''
typedJson.hookSpecificOutput?.hookEventName === 'WorktreeCreate'
? typedJson.hookSpecificOutput.worktreePath
: typedJson.systemMessage || ''
const blocked =
isSyncHookJSONOutput(json) && json.decision === 'block'
isSyncHookJSONOutput(json) && typedJson.decision === 'block'
logForDebugging(`${hookName} [callback] completed successfully`)
@@ -3316,11 +3318,12 @@ async function executeHooksOutsideREPL({
{ level: 'verbose' },
)
}
const typedHttpJson = httpJson as TypedSyncHookOutput | undefined
const jsonBlocked =
httpJson &&
!isAsyncHookJSONOutput(httpJson) &&
isSyncHookJSONOutput(httpJson) &&
httpJson.decision === 'block'
typedHttpJson?.decision === 'block'
// WorktreeCreate's consumer reads `output` as the bare filesystem
// path. Command hooks provide it via stdout; http hooks provide it
@@ -3331,8 +3334,8 @@ async function executeHooksOutsideREPL({
hookEvent === 'WorktreeCreate'
? httpJson &&
isSyncHookJSONOutput(httpJson) &&
httpJson.hookSpecificOutput?.hookEventName === 'WorktreeCreate'
? httpJson.hookSpecificOutput.worktreePath
typedHttpJson?.hookSpecificOutput?.hookEventName === 'WorktreeCreate'
? typedHttpJson.hookSpecificOutput.worktreePath
: ''
: httpResult.body
@@ -3408,11 +3411,12 @@ async function executeHooksOutsideREPL({
}
// Blocked if exit code 2 or JSON decision: 'block'
const typedJson = json as TypedSyncHookOutput | undefined
const jsonBlocked =
json &&
!isAsyncHookJSONOutput(json) &&
isSyncHookJSONOutput(json) &&
json.decision === 'block'
typedJson?.decision === 'block'
const blocked = result.status === 2 || !!jsonBlocked
// For successful hooks (exit code 0), use stdout; for failed hooks, use stderr
@@ -3422,13 +3426,13 @@ async function executeHooksOutsideREPL({
const watchPaths =
json &&
isSyncHookJSONOutput(json) &&
json.hookSpecificOutput &&
'watchPaths' in json.hookSpecificOutput
? json.hookSpecificOutput.watchPaths
typedJson?.hookSpecificOutput &&
'watchPaths' in typedJson.hookSpecificOutput
? (typedJson.hookSpecificOutput as { watchPaths?: string[] }).watchPaths
: undefined
const systemMessage =
json && isSyncHookJSONOutput(json) ? json.systemMessage : undefined
json && isSyncHookJSONOutput(json) ? typedJson?.systemMessage : undefined
return {
command: hook.command,
@@ -3685,13 +3689,18 @@ export async function executeStopFailureHooks(
const sessionId = getSessionId()
if (!hasHookForEvent('StopFailure', appState, sessionId)) return
const rawContent = lastMessage.message?.content
const lastAssistantText =
extractTextContent(lastMessage.message.content, '\n').trim() || undefined
(Array.isArray(rawContent)
? extractTextContent(rawContent as readonly { readonly type: string }[], '\n').trim()
: typeof rawContent === 'string'
? rawContent.trim()
: '') || undefined
// Some createAssistantAPIErrorMessage call sites omit `error` (e.g.
// image-size at errors.ts:431). Default to 'unknown' so matcher filtering
// at getMatchingHooks:1525 always applies.
const error = lastMessage.error ?? 'unknown'
const error = (lastMessage.error as string | undefined) ?? 'unknown'
const hookInput: StopFailureHookInput = {
...createBaseHookInput(undefined, undefined, toolUseContext),
hook_event_name: 'StopFailure',
@@ -3744,9 +3753,13 @@ export async function* executeStopHooks(
const lastAssistantMessage = messages
? getLastAssistantMessage(messages)
: undefined
const lastAssistantContent = lastAssistantMessage?.message?.content
const lastAssistantText = lastAssistantMessage
? extractTextContent(lastAssistantMessage.message.content, '\n').trim() ||
undefined
? (Array.isArray(lastAssistantContent)
? extractTextContent(lastAssistantContent as readonly { readonly type: string }[], '\n').trim()
: typeof lastAssistantContent === 'string'
? lastAssistantContent.trim()
: '') || undefined
: undefined
const hookInput: StopHookInput | SubagentStopHookInput = subagentId
@@ -4192,11 +4205,11 @@ export async function executeSessionEndHooks(
timeoutMs = TOOL_HOOK_EXECUTION_TIMEOUT_MS,
} = options || {}
const hookInput: SessionEndHookInput = {
const hookInput = {
...createBaseHookInput(undefined),
hook_event_name: 'SessionEnd',
hook_event_name: 'SessionEnd' as const,
reason,
}
} as unknown as SessionEndHookInput
const results = await executeHooksOutsideREPL({
getAppState,
@@ -4366,12 +4379,12 @@ export function executeFileChangedHooks(
watchPaths: string[]
systemMessages: string[]
}> {
const hookInput: FileChangedHookInput = {
const hookInput = {
...createBaseHookInput(undefined),
hook_event_name: 'FileChanged',
hook_event_name: 'FileChanged' as const,
file_path: filePath,
event,
}
} as unknown as FileChangedHookInput
return executeEnvHooks(hookInput, timeoutMs)
}
@@ -4503,28 +4516,32 @@ function parseElicitationHookOutput(
return {}
}
// Cast to typed interface for type-safe property access
const typedParsed = parsed as TypedSyncHookOutput
// Check for top-level decision: 'block' (exit code 0 + JSON block)
if (parsed.decision === 'block' || result.blocked) {
if (typedParsed.decision === 'block' || result.blocked) {
return {
blockingError: {
blockingError: parsed.reason || 'Elicitation blocked by hook',
blockingError: typedParsed.reason || 'Elicitation blocked by hook',
command: result.command,
},
}
}
const specific = parsed.hookSpecificOutput
const specific = typedParsed.hookSpecificOutput
if (!specific || specific.hookEventName !== expectedEventName) {
return {}
}
if (!specific.action) {
if (!('action' in specific) || !(specific as { action?: string }).action) {
return {}
}
const typedSpecific = specific as { action: string; content?: Record<string, unknown> }
const response: ElicitationResponse = {
action: specific.action,
content: specific.content as ElicitationResponse['content'] | undefined,
action: typedSpecific.action as ElicitationResponse['action'],
content: typedSpecific.content as ElicitationResponse['content'] | undefined,
}
const out: {
@@ -4532,10 +4549,10 @@ function parseElicitationHookOutput(
blockingError?: HookBlockingError
} = { response }
if (specific.action === 'decline') {
if (typedSpecific.action === 'decline') {
out.blockingError = {
blockingError:
parsed.reason ||
typedParsed.reason ||
(expectedEventName === 'Elicitation'
? 'Elicitation denied by hook'
: 'Elicitation result blocked by hook'),