style: 完成所有文件的lint

This commit is contained in:
claude-code-best
2026-05-01 21:39:30 +08:00
parent d136872cc9
commit 6182015005
1333 changed files with 68255 additions and 77882 deletions

View File

@@ -1,24 +1,21 @@
import { feature } from 'bun:bundle'
import * as React from 'react'
import {
getAllowedChannels,
getQuestionPreviewFormat,
} from 'src/bootstrap/state.js'
import { MessageResponse } from 'src/components/MessageResponse.js'
import { BLACK_CIRCLE } from 'src/constants/figures.js'
import { getModeColor } from 'src/utils/permissions/PermissionMode.js'
import { z } from 'zod/v4'
import { Box, Text } from '@anthropic/ink'
import type { Tool } from 'src/Tool.js'
import { buildTool, type ToolDef } from 'src/Tool.js'
import { lazySchema } from 'src/utils/lazySchema.js'
import { feature } from 'bun:bundle';
import * as React from 'react';
import { getAllowedChannels, getQuestionPreviewFormat } from 'src/bootstrap/state.js';
import { MessageResponse } from 'src/components/MessageResponse.js';
import { BLACK_CIRCLE } from 'src/constants/figures.js';
import { getModeColor } from 'src/utils/permissions/PermissionMode.js';
import { z } from 'zod/v4';
import { Box, Text } from '@anthropic/ink';
import type { Tool } from 'src/Tool.js';
import { buildTool, type ToolDef } from 'src/Tool.js';
import { lazySchema } from 'src/utils/lazySchema.js';
import {
ASK_USER_QUESTION_TOOL_CHIP_WIDTH,
ASK_USER_QUESTION_TOOL_NAME,
ASK_USER_QUESTION_TOOL_PROMPT,
DESCRIPTION,
PREVIEW_FEATURE_PROMPT,
} from './prompt.js'
} from './prompt.js';
const questionOptionSchema = lazySchema(() =>
z.object({
@@ -39,7 +36,7 @@ const questionOptionSchema = lazySchema(() =>
'Optional preview content rendered when this option is focused. Use for mockups, code snippets, or visual comparisons that help users compare options. See the tool description for the expected content format.',
),
}),
)
);
const questionSchema = lazySchema(() =>
z.object({
@@ -67,55 +64,44 @@ const questionSchema = lazySchema(() =>
'Set to true to allow the user to select multiple options instead of just one. Use when choices are not mutually exclusive.',
),
}),
)
);
const annotationsSchema = lazySchema(() => {
const annotationSchema = z.object({
preview: z
.string()
.optional()
.describe(
'The preview content of the selected option, if the question used previews.',
),
notes: z
.string()
.optional()
.describe('Free-text notes the user added to their selection.'),
})
.describe('The preview content of the selected option, if the question used previews.'),
notes: z.string().optional().describe('Free-text notes the user added to their selection.'),
});
return z
.record(z.string(), annotationSchema)
.optional()
.describe(
'Optional per-question annotations from the user (e.g., notes on preview selections). Keyed by question text.',
)
})
);
});
const UNIQUENESS_REFINE = {
check: (data: {
questions: { question: string; options: { label: string }[] }[]
}) => {
const questions = data.questions.map(q => q.question)
check: (data: { questions: { question: string; options: { label: string }[] }[] }) => {
const questions = data.questions.map(q => q.question);
if (questions.length !== new Set(questions).size) {
return false
return false;
}
for (const question of data.questions) {
const labels = question.options.map(opt => opt.label)
const labels = question.options.map(opt => opt.label);
if (labels.length !== new Set(labels).size) {
return false
return false;
}
}
return true
return true;
},
message:
'Question texts must be unique, option labels must be unique within each question',
} as const
message: 'Question texts must be unique, option labels must be unique within each question',
} as const;
const commonFields = lazySchema(() => ({
answers: z
.record(z.string(), z.string())
.optional()
.describe('User answers collected by the permission component'),
answers: z.record(z.string(), z.string()).optional().describe('User answers collected by the permission component'),
annotations: annotationsSchema(),
metadata: z
.object({
@@ -127,32 +113,24 @@ const commonFields = lazySchema(() => ({
),
})
.optional()
.describe(
'Optional metadata for tracking and analytics purposes. Not displayed to user.',
),
}))
.describe('Optional metadata for tracking and analytics purposes. Not displayed to user.'),
}));
const inputSchema = lazySchema(() =>
z
.strictObject({
questions: z
.array(questionSchema())
.min(1)
.max(4)
.describe('Questions to ask the user (1-4 questions)'),
questions: z.array(questionSchema()).min(1).max(4).describe('Questions to ask the user (1-4 questions)'),
...commonFields(),
})
.refine(UNIQUENESS_REFINE.check, {
message: UNIQUENESS_REFINE.message,
}),
)
type InputSchema = ReturnType<typeof inputSchema>
);
type InputSchema = ReturnType<typeof inputSchema>;
const outputSchema = lazySchema(() =>
z.object({
questions: z
.array(questionSchema())
.describe('The questions that were asked'),
questions: z.array(questionSchema()).describe('The questions that were asked'),
answers: z
.record(z.string(), z.string())
.describe(
@@ -160,23 +138,19 @@ const outputSchema = lazySchema(() =>
),
annotations: annotationsSchema(),
}),
)
type OutputSchema = ReturnType<typeof outputSchema>
);
type OutputSchema = ReturnType<typeof outputSchema>;
// SDK schemas are identical to internal schemas now that `preview` and
// `annotations` are public (configurable via `toolConfig.askUserQuestion`).
export const _sdkInputSchema = inputSchema
export const _sdkOutputSchema = outputSchema
export const _sdkInputSchema = inputSchema;
export const _sdkOutputSchema = outputSchema;
export type Question = z.infer<ReturnType<typeof questionSchema>>
export type QuestionOption = z.infer<ReturnType<typeof questionOptionSchema>>
export type Output = z.infer<OutputSchema>
export type Question = z.infer<ReturnType<typeof questionSchema>>;
export type QuestionOption = z.infer<ReturnType<typeof questionOptionSchema>>;
export type Output = z.infer<OutputSchema>;
function AskUserQuestionResultMessage({
answers,
}: {
answers: Output['answers']
}): React.ReactNode {
function AskUserQuestionResultMessage({ answers }: { answers: Output['answers'] }): React.ReactNode {
return (
<Box flexDirection="column" marginTop={1}>
<Box flexDirection="row">
@@ -193,7 +167,7 @@ function AskUserQuestionResultMessage({
</Box>
</MessageResponse>
</Box>
)
);
}
export const AskUserQuestionTool: Tool<InputSchema, Output> = buildTool({
@@ -202,25 +176,25 @@ export const AskUserQuestionTool: Tool<InputSchema, Output> = buildTool({
maxResultSizeChars: 100_000,
shouldDefer: true,
async description() {
return DESCRIPTION
return DESCRIPTION;
},
async prompt() {
const format = getQuestionPreviewFormat()
const format = getQuestionPreviewFormat();
if (format === undefined) {
// SDK consumer that hasn't opted into a preview format — omit preview
// guidance (they may not render the field at all).
return ASK_USER_QUESTION_TOOL_PROMPT
return ASK_USER_QUESTION_TOOL_PROMPT;
}
return ASK_USER_QUESTION_TOOL_PROMPT + PREVIEW_FEATURE_PROMPT[format]
return ASK_USER_QUESTION_TOOL_PROMPT + PREVIEW_FEATURE_PROMPT[format];
},
get inputSchema(): InputSchema {
return inputSchema()
return inputSchema();
},
get outputSchema(): OutputSchema {
return outputSchema()
return outputSchema();
},
userFacingName() {
return ''
return '';
},
isEnabled() {
// When --channels is active the user is likely on Telegram/Discord, not
@@ -228,59 +202,56 @@ export const AskUserQuestionTool: Tool<InputSchema, Output> = buildTool({
// the keyboard. Channel permission relay already skips
// requiresUserInteraction() tools (interactiveHandler.ts) so there's
// no alternate approval path.
if (
(feature('KAIROS') || feature('KAIROS_CHANNELS')) &&
getAllowedChannels().length > 0
) {
return false
if ((feature('KAIROS') || feature('KAIROS_CHANNELS')) && getAllowedChannels().length > 0) {
return false;
}
return true
return true;
},
isConcurrencySafe() {
return true
return true;
},
isReadOnly() {
return true
return true;
},
toAutoClassifierInput(input) {
return input.questions.map(q => q.question).join(' | ')
return input.questions.map(q => q.question).join(' | ');
},
requiresUserInteraction() {
return true
return true;
},
async validateInput({ questions }) {
if (getQuestionPreviewFormat() !== 'html') {
return { result: true }
return { result: true };
}
for (const q of questions) {
for (const opt of q.options) {
const err = validateHtmlPreview(opt.preview)
const err = validateHtmlPreview(opt.preview);
if (err) {
return {
result: false,
message: `Option "${opt.label}" in question "${q.question}": ${err}`,
errorCode: 1,
}
};
}
}
}
return { result: true }
return { result: true };
},
async checkPermissions(input) {
return {
behavior: 'ask' as const,
message: 'Answer questions?',
updatedInput: input,
}
};
},
renderToolUseMessage() {
return null
return null;
},
renderToolUseProgressMessage() {
return null
return null;
},
renderToolResultMessage({ answers }, _toolUseID) {
return <AskUserQuestionResultMessage answers={answers} />
return <AskUserQuestionResultMessage answers={answers} />;
},
renderToolUseRejectedMessage() {
return (
@@ -288,55 +259,55 @@ export const AskUserQuestionTool: Tool<InputSchema, Output> = buildTool({
<Text color={getModeColor('default')}>{BLACK_CIRCLE}&nbsp;</Text>
<Text>User declined to answer questions</Text>
</Box>
)
);
},
renderToolUseErrorMessage() {
return null
return null;
},
async call({ questions, answers = {}, annotations }, _context) {
return {
data: { questions, answers, ...(annotations && { annotations }) },
}
};
},
mapToolResultToToolResultBlockParam({ answers, annotations }, toolUseID) {
const answersText = Object.entries(answers)
.map(([questionText, answer]) => {
const annotation = annotations?.[questionText]
const parts = [`"${questionText}"="${answer}"`]
const annotation = annotations?.[questionText];
const parts = [`"${questionText}"="${answer}"`];
if (annotation?.preview) {
parts.push(`selected preview:\n${annotation.preview}`)
parts.push(`selected preview:\n${annotation.preview}`);
}
if (annotation?.notes) {
parts.push(`user notes: ${annotation.notes}`)
parts.push(`user notes: ${annotation.notes}`);
}
return parts.join(' ')
return parts.join(' ');
})
.join(', ')
.join(', ');
return {
type: 'tool_result',
content: `User has answered your questions: ${answersText}. You can now continue with the user's answers in mind.`,
tool_use_id: toolUseID,
}
};
},
} satisfies ToolDef<InputSchema, Output>)
} satisfies ToolDef<InputSchema, Output>);
// Lightweight HTML fragment check. Not a parser — HTML5 parsers are
// error-recovering by spec and accept anything. We're checking model intent
// (did it emit HTML?) and catching the specific things we told it not to do.
function validateHtmlPreview(preview: string | undefined): string | null {
if (preview === undefined) return null
if (preview === undefined) return null;
if (/<\s*(html|body|!doctype)\b/i.test(preview)) {
return 'preview must be an HTML fragment, not a full document (no <html>, <body>, or <!DOCTYPE>)'
return 'preview must be an HTML fragment, not a full document (no <html>, <body>, or <!DOCTYPE>)';
}
// SDK consumers typically set this via innerHTML — disallow executable/style
// tags so a preview can't run code or restyle the host page. Inline event
// handlers (onclick etc.) are still possible; consumers should sanitize.
if (/<\s*(script|style)\b/i.test(preview)) {
return 'preview must not contain <script> or <style> tags. Use inline styles via the style attribute if needed.'
return 'preview must not contain <script> or <style> tags. Use inline styles via the style attribute if needed.';
}
if (!/<[a-z][^>]*>/i.test(preview)) {
return 'preview must contain HTML (previewFormat is set to "html"). Wrap content in a tag like <div> or <pre>.'
return 'preview must contain HTML (previewFormat is set to "html"). Wrap content in a tag like <div> or <pre>.';
}
return null
return null;
}

View File

@@ -1,3 +1,3 @@
// Auto-generated type stub — replace with real implementation
export type getAllowedChannels = any;
export type getQuestionPreviewFormat = any;
export type getAllowedChannels = any
export type getQuestionPreviewFormat = any

View File

@@ -1,2 +1,2 @@
// Auto-generated type stub — replace with real implementation
export type MessageResponse = any;
export type MessageResponse = any

View File

@@ -1,2 +1,2 @@
// Auto-generated type stub — replace with real implementation
export type BLACK_CIRCLE = any;
export type BLACK_CIRCLE = any

View File

@@ -1,2 +1,2 @@
// Auto-generated type stub — replace with real implementation
export type getModeColor = any;
export type getModeColor = any